/*
* 作者: wupengxin
* 创建时间: 2016-10-03 20:54:02
* graylog的客户端 通过graylog的gelf格式将数据发送出去
 */
package outputplug

import (
	"common"
	"errors"
	"net"
	"strings"
	"time"
	"encoding/json"
	"os"
	"strconv"
)

type GrayLogClient struct {
	Host    string
	Workers int
	Channel chan map[string]string
}

func createCon(protocal, host string, retry time.Duration) net.Conn {
	con, e := net.Dial(protocal, host)
	for e != nil {
		// 说明连接失败 重新连接
		time.Sleep(retry*time.Second)
		con, e = net.Dial(protocal, host)
	}
	return con
}

func (this *GrayLogClient) SendMessage() {

	strs := strings.Split(this.Host, "://")
	pro:= string(strs[0])
	host := string(strs[1])
	protocal := strings.ToLower(pro)

	if protocal != "tcp" && protocal != "udp" {
		// 只支持tcp和udp协议
		common.Exit(errors.New("未识别的协议"))
	}
	// 创建网络连接
	con := createCon(protocal, string(host), 5)
	defer con.Close()

	for {
		result, ok := <-this.Channel
		if !ok {
			// 说明chan被关闭 退出gorounte
			common.Debug(errors.New("channel closed exit gorounte"))
			return
		}
		message := this.generateGelfMsg(result)
		// 将该消息发送出去
		for {
			resultLen, e := con.Write([]byte(message))
			if e != nil {
				con.Close()
				con = createCon(protocal, host, 5)
				continue
			}
			if resultLen < len(message) {
				message = message[resultLen:]
				continue
			} else {
				break
			}
		}
	}

}

func (this *GrayLogClient)generateGelfMsg(buf map[string]string) string  {
	type gelf struct {
		Contant string `json:"contant"`
		LogTimestamp time.Time `json:"_logtimestamp"`
		Appid string `json:"_appid"`
		ContainerId string `json:"_container_id"`
		Version string `json:"version"`
		Host string `json:"host"`
		File string `json:"file"`
		ShortMsg string `json:"short_message"`
	}
	message := gelf{Contant: buf["contant"], Appid: buf["appid"], ContainerId: buf["container_id"],
		LogTimestamp: time.Now()}
	message.File = buf["file"]
	message.Host, _ = os.Hostname()
	message.Version = "1.1"
	message.ShortMsg = buf["contant"][0:10]

	msg, _ := json.Marshal(message)
	return string(msg)+`\0`
}

//配置es信息
func configGrayCli(conf map[string]string, this *GrayLogClient)  error{
	// 读取配置文件 设置属性
	if v, ok := conf["hosts"]; ok{
		this.Host = v
	}else {
		err := errors.New("读取配置文件错误")
		common.Debug(err)
		return  err
	}

	// workers
	if v, ok:= conf["workers"]; ok{
		this.Workers, _= strconv.Atoi(v)
	}else {
		this.Workers = 1
	}
	return nil
}
// 创建一个es对象
func NewGrayCli(conf map[string]string) (*GrayLogClient, error) {
	cli := GrayLogClient{}
	err := configGrayCli(conf, &cli)
	return &cli, err
}

func (this *GrayLogClient)PutOutStream(conf map[string]string, channels []chan map[string]string)  error{
	// 读取配置
	cli, e := NewGrayCli(conf)

	if e!=nil{
		common.Debug(e)
		return e
	}

	for i := 0; i < cli.Workers; i++{
		cli.Channel = channels[0]
		go cli.SendMessage()
	}

	return nil
}

// 实现解析配置接口
func (this *GrayLogClient)ConfigPlus(conf interface{}) (map[string]string, error)  {
	dat := conf.(map[string]interface{})
	tmp := make(map[string]string)
	// 开始解析配置
	if _, ok := dat["hosts"]; !ok{
		return nil, errors.New("graylog host must be conf")
	}else {
		tmp["hosts"] = dat["hosts"].(string)
	}

	if v, ok := dat["workers"]; ok{
		tmp["workers"] = v.(string)
	}
	return tmp, nil
}
